package com.janetfilter.plugins.power;

import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Period;
import java.time.Year;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Objects;
import java.util.SimpleTimeZone;
import java.util.TimeZone;

/**
 * jdk8
 *
 * @author gongxw
 * @version 0
 * @since 2018-12-21 0021 下午 05:14
 */
public abstract class DateTimeHelper {
    public static final String LOCALE = "zh_CN";
    public static final String TZ_ID = "Asia/Shanghai";
    public static final TimeZone TZ = SimpleTimeZone.getTimeZone(TZ_ID);
    /**
     * Hours per day.
     */
    public static final int HOURS_PER_DAY = 24;
    /**
     * Minutes per hour.
     */
    public static final int MINUTES_PER_HOUR = 60;
    /**
     * Minutes per day.
     */
    public static final int MINUTES_PER_DAY = MINUTES_PER_HOUR * HOURS_PER_DAY;
    /**
     * Seconds per minute.
     */
    public static final int SECONDS_PER_MINUTE = 60;
    /**
     * Seconds per hour.
     */
    public static final int SECONDS_PER_HOUR = SECONDS_PER_MINUTE * MINUTES_PER_HOUR;
    /**
     * Seconds per day.
     */
    public static final int SECONDS_PER_DAY = SECONDS_PER_HOUR * HOURS_PER_DAY;
    /**
     * Milliseconds per day.
     */
    public static final long MILLIS_PER_DAY = SECONDS_PER_DAY * 1000L;
    /**
     * Microseconds per day.
     */
    public static final long MICROS_PER_DAY = SECONDS_PER_DAY * 1000_000L;
    /**
     * Nanos per millisecond.
     */
    public static final long NANOS_PER_MILLI = 1000_000L;
    /**
     * Nanos per second.
     */
    public static final long NANOS_PER_SECOND = 1000_000_000L;
    /**
     * Nanos per minute.
     */
    public static final long NANOS_PER_MINUTE = NANOS_PER_SECOND * SECONDS_PER_MINUTE;
    /**
     * Nanos per hour.
     */
    public static final long NANOS_PER_HOUR = NANOS_PER_MINUTE * MINUTES_PER_HOUR;
    /**
     * Nanos per day.
     */
    public static final long NANOS_PER_DAY = NANOS_PER_HOUR * HOURS_PER_DAY;

    public static SimpleDateFormat createSimpleDateFormat(final String format) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
        simpleDateFormat.setTimeZone(TZ);
        return simpleDateFormat;
    }

    public static GregorianCalendar creatGregorianCalendar() {
        return creatGregorianCalendar(new Date(Long.MIN_VALUE));
    }

    public static GregorianCalendar creatGregorianCalendar(Date date) {
        GregorianCalendar gc = new GregorianCalendar();
        gc.setGregorianChange(date);
        gc.setTimeZone(TZ);
        return gc;
    }

    public static String now(final DatePattern datePattern) {
        return format(Instant.now(), Objects.requireNonNullElse(datePattern, BaseDatePattern.MID_DATA_FORMAT));
    }

    public static String format(final GregorianCalendar gregorianCalendar, final DatePattern datePattern) {
        return format(gregorianCalendar.getTime().toInstant(), datePattern);
    }

    public static String format(final Date date, final DatePattern datePattern) {
        return format(date.toInstant(), datePattern);
    }

    public static String format(final Instant instant, final DatePattern datePattern) {
        return DateTimeFormatter.ofPattern(datePattern.getText()).format(instant.atZone(ZoneId.systemDefault()));
    }

    public static String add(final GregorianCalendar gc,
                             final int field,
                             final int amount,
                             final DatePattern datePattern) {
        gc.add(field, amount);
        return DateTimeHelper.format(gc, datePattern);
    }

    public static String addMinute(GregorianCalendar gc, final int amount, final DatePattern datePattern) {
        return add(gc, GregorianCalendar.MINUTE, 10, datePattern);
    }

    public static int nowYear() {
        return Year.now().getValue();
    }

    public static LocalTime string2LocalTime(final String date, final DatePattern datePattern) {
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(datePattern.getText());
        return LocalTime.parse(date, dateTimeFormatter);
    }

    public static LocalDateTime string2LocalDateTime(final String date, final DatePattern datePattern) {
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(datePattern.getText());
        return LocalDateTime.parse(date, dateTimeFormatter);
    }

    public static Date string2DateTime(final String date, final DatePattern datePattern) {
        LocalDateTime localDateTime = string2LocalDateTime(date, datePattern);
        Instant instant = localDateTime.atZone(ZoneId.systemDefault()).toInstant();
        return Date.from(instant);
    }

    public static LocalDate string2LocalDate(final String date, final DatePattern datePattern) {
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(datePattern.getText());
        return LocalDate.parse(date, dateTimeFormatter);
    }

    public static Date string2Date(final String date, final DatePattern datePattern) {
        LocalDate localDate = string2LocalDate(date, datePattern);
        Instant instant = localDate.atStartOfDay(ZoneId.systemDefault()).toInstant();
        return Date.from(instant);
    }

    public static GregorianCalendar string2Calendar(final String date, final DatePattern datePattern) {
        GregorianCalendar calendar = creatGregorianCalendar();
        calendar.setTime(string2Date(date, datePattern));
        return calendar;
    }

    public static String computeMinute(final int min) {
        StringBuilder sb = new StringBuilder();
        long days = min / 24 / 60;
        long hours = (min % (24 * 60)) / 60;
        long minutes = min % 60;
        if (days > 0) {
            sb.append(days).append("天");
        }
        if (hours > 0) {
            sb.append(hours).append("小时");
        }
        if (minutes > 0) {
            sb.append(minutes).append("分钟");
        }
        return sb.toString();
    }

    public static LocalDateTime dateToLocalDateTime(Date date) {
        return LocalDateTime.ofInstant(date.toInstant(), ZoneOffset.systemDefault());
    }

    public static LocalDate dateToLocalDate(Date date) {
        return LocalDate.ofInstant(date.toInstant(), ZoneOffset.systemDefault());
    }

    public static LocalTime dateToLocalTime(Date date) {
        return LocalTime.ofInstant(date.toInstant(), ZoneOffset.systemDefault());
    }

    public static String periodToLocalString(Period period) {
        if (period == Period.ZERO) {
            return "0日";
        } else {
            StringBuilder buf = new StringBuilder();
            if (period.getYears() != 0) {
                buf.append(period.getYears()).append('年');
            }
            if (period.getMonths() != 0) {
                buf.append(period.getMonths()).append('月');
            }
            if (period.getDays() != 0) {
                buf.append(period.getDays()).append('日');
            }
            return buf.toString();
        }
    }

    public static String durationToLocalString(Duration duration) {
        if (duration == Duration.ZERO) {
            return "0秒";
        }
        long effectiveTotalSecs = duration.getSeconds();
        if (duration.getSeconds() < 0 && duration.getNano() > 0) {
            effectiveTotalSecs++;
        }
        long days = effectiveTotalSecs / SECONDS_PER_DAY;
        int hours = (int) ((effectiveTotalSecs % SECONDS_PER_DAY) / SECONDS_PER_HOUR);
        int minutes = (int) ((effectiveTotalSecs % SECONDS_PER_HOUR) / SECONDS_PER_MINUTE);
        int secs = (int) (effectiveTotalSecs % SECONDS_PER_MINUTE);
        StringBuilder buf = new StringBuilder(24);
        if (days != 0) {
            buf.append(days).append("日");
        }
        if (hours != 0) {
            buf.append(hours).append("小时");
        }
        if (minutes != 0) {
            buf.append(minutes).append("分钟");
        }
        if (secs == 0 && duration.getNano() == 0 && buf.length() > 2) {
            return buf.toString();
        }
        if (duration.getSeconds() < 0 && duration.getNano() > 0) {
            if (secs == 0) {
                buf.append("-0");
            } else {
                buf.append(secs);
            }
        } else {
            buf.append(secs);
        }
        if (duration.getNano() > 0) {
            int pos = buf.length();
            if (duration.getSeconds() < 0) {
                buf.append(2 * NANOS_PER_SECOND - duration.getNano());
            } else {
                buf.append(duration.getNano() + NANOS_PER_SECOND);
            }
            while (buf.charAt(buf.length() - 1) == '0') {
                buf.setLength(buf.length() - 1);
            }
            buf.setCharAt(pos, '.');
        }
        buf.append("秒");
        return buf.toString();
    }
}
